#include <string>
#include <clang/AST/RecursiveASTVisitor.h>
#include "checkers/UnInitVarChecker.h"
#include "DetectMe/OperationDetection.h"
#include "checkers/AlignCacheline.h"
#include "DetectMe/Expression.h"

using namespace clang;
using namespace std;

extern ReportSpace::Report external_bug_report;
string getStringOfExpr(Expr *expr)
{
    LangOptions L0;
    L0.CPlusPlus = 1;
    std::string buffer1;
    llvm::raw_string_ostream strout1(buffer1);
    expr->printPretty(strout1, nullptr, PrintingPolicy(L0));
    return strout1.str();
}
Expr *UnInitVarChecker::getBase(MemberExpr *member_expr)
{
    Expr *member_base_expr = remove_cast(member_expr->getBase());
    if (clang::DeclRefExpr *expr = llvm::dyn_cast<clang::DeclRefExpr>(member_base_expr))
        return expr;
    else if (member_base_expr->getStmtClass() == clang::Stmt::MemberExprClass)
    {
        MemberExpr *member_tmp = llvm::dyn_cast<clang::MemberExpr>(member_base_expr);
        return getBase(member_tmp);
    }
    else if(clang::ParenExpr *paren_expr = llvm::dyn_cast<clang::ParenExpr>(member_base_expr))
    {
        if(paren_expr->getSubExpr()->getStmtClass() == clang::Stmt::MemberExprClass){
            MemberExpr *member_tmp = llvm::dyn_cast<clang::MemberExpr>(paren_expr->getSubExpr());
            return getBase(member_tmp);
        }
        else 
          return paren_expr->getSubExpr();

    }
    else if (member_base_expr->getStmtClass() == clang::Stmt::ArraySubscriptExprClass)
    {
        clang::ArraySubscriptExpr *array_tmp = llvm::dyn_cast<clang::ArraySubscriptExpr>(member_base_expr);
        Expr *array_base = remove_cast(array_tmp->getBase());
        if(array_base->getStmtClass() == clang::Stmt::DeclRefExprClass)
          return array_base;
        else if(array_base->getStmtClass() == clang::Stmt::MemberExprClass)
        {
            MemberExpr *member_tmp = llvm::dyn_cast<clang::MemberExpr>(array_base);
            return getBase(member_tmp);
        }
          
    }
    return nullptr;
}
void UnInitVarChecker::check()
{
    unsigned funcNums = manager->getFunctions(true).size();
    int i = 0;
    for (ASTFunction *AF : resource->getFunctions(true))
    {
        string filename = "";
        FunctionDecl *FD = manager->getFunctionDecl(AF);
        const clang::SourceManager &SM = FD->getASTContext().getSourceManager();
        SourceLocation SL = FD->getBeginLoc();
        filename = SL.printToString(SM);
        if (filename.find(".h") != string::npos || filename.find("include") != string::npos)
        {
            funcNums--;
            continue;
        }
        //  FD->dump();
        i++;
        process_bar(float(i) / funcNums);
        readConfig();
        if(FD->hasBody())
          FD = FD->getDefinition();
        AF = manager->getASTFunction(FD);
        unique_ptr<CFG> &cfg = manager->getCFG(AF);
        OperationDetection missingCheck(manager, call_graph, AF);
        missingCheck.set_max_call_depth(depth + 1);

        if (cfg == nullptr)
            continue;
        list<string> member_list;
        for (auto blk_it = cfg->begin(); blk_it != cfg->end(); blk_it++)
        {
            clang::CFGBlock *blk = *blk_it;
            for (auto it = blk->rbegin(); it != blk->rend(); it++)
            {
                if (it->getKind() == clang::CFGElement::Statement)
                {
                    Stmt *stmt = const_cast<Stmt *>(it->castAs<CFGStmt>().getStmt());
                    //stmt->dump();
                    FindStructUse *MemberuseExprs = new FindStructUse();
                    MemberuseExprs->TraverseStmt(stmt);
                    FindStructUseCut *Memberuse_cutExprs = new FindStructUseCut();
                    Memberuse_cutExprs->TraverseStmt(stmt);
                    vector<MemberExpr *> tmp_memberexprs = getUseMemexpr(MemberuseExprs->getAllExprs(), Memberuse_cutExprs->getCutExprs());
                    
                    if (!tmp_memberexprs.empty())
                    {
                        for (auto MemberuseExpr : tmp_memberexprs)
                        {
                            const Expr *member_base_expr = remove_cast(getBase(MemberuseExpr));
                            auto list_it = find(member_list.begin(), member_list.end(), getStringOfExpr(MemberuseExpr));
                            if (list_it != member_list.end())
                                continue;
                            member_list.insert(member_list.end(), getStringOfExpr(MemberuseExpr));
                            
                            const SourceLocation expr_SL = MemberuseExpr->getExprLoc();
                            const clang::SourceManager &SM = FD->getASTContext().getSourceManager();
                            unsigned int lineNum = SM.getExpansionLineNumber(expr_SL);//
                            string exprname = getStringOfExpr(MemberuseExpr);
                            bool has_checked = false;
                            missingCheck.set_operation<AssignOperation>();
                            missingCheck.set_use_point(blk, it);
                            missingCheck.use_point()->set_value_holder<ClangMemberExprValueHolder>(llvm::dyn_cast<clang::MemberExpr>(MemberuseExpr));
                            if (missingCheck.detect(OperationDetection::Forall))
                                has_checked = true;
                            if (!has_checked && member_base_expr)
                            {
                                Expr* tmp_member_base = const_cast<Expr *>(member_base_expr);
                                // tmp_member_base->dump();
                                missingCheck.set_operation<StructInitOperation>();
                                missingCheck.set_use_point(blk, it);
                                if(tmp_member_base){
                                    if (clang::DeclRefExpr *decl_tmp = llvm::dyn_cast<clang::DeclRefExpr>(tmp_member_base)){
                                        const clang::ValueDecl *value_decl =
                                            llvm::cast<clang::DeclRefExpr>(tmp_member_base)->getDecl();
                                        if (value_decl->getKind() == clang::Decl::Var) {
                                            const clang::VarDecl *var_decl = llvm::cast<clang::VarDecl>(value_decl);
                                            const clang::Expr *init_expr = var_decl->getInit();
                                            if(init_expr && init_expr->getStmtClass() == clang::Stmt::InitListExprClass)
                                                continue;     
                                        }
                                        missingCheck.use_point()->set_value_holder<ClangDeclRefExprValueHolder>(decl_tmp);
                                    }

                                    else
                                    {
                                        ReportBug(FD, expr_SL, exprname, filename, "");
                                        continue;
                                    }
                                    if (missingCheck.detect(OperationDetection::Forall))
                                        has_checked = true;
                                }
                            }
                            if (!has_checked)
                            {
                                ReportBug(FD, expr_SL, exprname, filename, "");
                            }
                        }
                    }
                    vector<Expr *> tmp_exprs = selectExpr(MemberuseExprs->getcontainer_struct());
                    if (!tmp_exprs.empty())
                    {
                        for (Expr *container_expr : tmp_exprs)
                        {
                            if (container_expr->getStmtClass() != clang::Stmt::DeclRefExprClass)
                                continue;
                            /*auto list_it = find(member_list.begin(), member_list.end(), getStringOfExpr(container_expr));
                            if (list_it != member_list.end())
                                continue;   
                            member_list.insert(member_list.end(), getStringOfExpr(container_expr));*/
                            bool has_checked = false;
                            const SourceLocation expr_SL = container_expr->getExprLoc();
                            string exprname = getStringOfExpr(container_expr);
                            missingCheck.set_operation<StructInitOperation>();
                            missingCheck.set_use_point(blk, it);
                            missingCheck.use_point()->set_value_holder<ClangDeclRefExprValueHolder>(llvm::dyn_cast<clang::DeclRefExpr>(container_expr));
                            if (missingCheck.detect(OperationDetection::Forall))
                                has_checked = true;
                            if (!has_checked)
                            {
                                has_checked = true;
                                missingCheck.set_use_point(blk, it);
                                VarDecl *var = llvm::dyn_cast<VarDecl>((llvm::dyn_cast<clang::DeclRefExpr>(container_expr))->getDecl());
                                RecordDecl *struct_record;
                                if (var->getType()->isPointerType())
                                    // var->getType()->getPointeeType()->getAsStructureType()->dump();
                                    struct_record = var->getType()->getPointeeType()->getAsStructureType()->getDecl();
                                else if (var->getType()->isStructureType())
                                    struct_record = var->getType()->getAsStructureType()->getDecl();
                                if (struct_record->field_empty())
                                    has_checked = true;
                                else
                                {
                                    for (FieldDecl *fd : struct_record->fields())
                                    {
                                        // <<fd->getNameAsString() << endl;
                                        MemberExpr *member = getMemberExprByFieldDecl(fd, filename, FD);
                                        if (!member)
                                            has_checked = false;
                                        else
                                        {
                                            missingCheck.set_operation<AllMemberAssignOperation>(fd);
                                            missingCheck.use_point()->set_value_holder<ClangDeclRefExprValueHolder>(llvm::dyn_cast<clang::DeclRefExpr>(container_expr));
                                            if (!missingCheck.detect(OperationDetection::Forall))
                                                has_checked = false; //如果有一个变量没有初始化就报错
                                        }
                                    }
                                }
                            }
                            if (!has_checked)
                                ReportBug(FD, expr_SL, exprname, filename, "");
                        }
                    }
                }
            }
        }
    }
}

vector<MemberExpr *> UnInitVarChecker::getUseMemexpr(vector<MemberExpr *> allexpr, vector<MemberExpr *> cutexpr)
{
    vector<MemberExpr *> useexpr;
    if (!allexpr.empty() && !cutexpr.empty())
    {
        for (MemberExpr *expr : allexpr)
        {
            auto it = find(cutexpr.begin(), cutexpr.end(), expr);
            if (it != cutexpr.end())
            {
                auto it = find(cutexpr.begin(), cutexpr.end(), expr);
                if (it != cutexpr.end())
                {
                    auto it_all = find(allexpr.begin(), allexpr.end(), expr);
                    if (it_all != allexpr.end())
                        allexpr.erase(it_all);
                    else
                        return useexpr;
                }
            }
        }
    }
    return allexpr;
}

void UnInitVarChecker::ReportBug(FunctionDecl *FD, const SourceLocation SL, string exprname, string filename, string desc) //desc为对error的描述
{
    const clang::SourceManager &SM = FD->getASTContext().getSourceManager();
    unsigned int lineNum = SM.getExpansionLineNumber(SL);
    unsigned int columnNum = SM.getExpansionColumnNumber(SL);
    ReportSpace::Defect *def = new ReportSpace::Defect(ReportSpace::DefectName::UninitializedStructureMember,
                                                       ReportSpace::DefectType::Error,
                                                       filename, lineNum, columnNum,
                                                       exprname);
    def->addDesc("");
    external_bug_report.addToReport(def);
}
void UnInitVarChecker::readConfig()
{
    BlockConfigsType ptrConfig = configure->getOptionBlock("UninitializedStructureMemberChecker");

    for (auto config : ptrConfig)
    {
        if (config.first == "depth")
        {
            depth = stoi(config.second);
            continue;
        }
        if (config.first == "enqueFunc")
        {
            string funcs;
            int param;
            map<string, int> func_param_map;
            funcs = config.second;
            funcs = funcs.substr(1,funcs.length()-1);
            int pos_begin = 0;
            int pos_end = 0;
            int size = funcs.size();
            string func_param;
            pos_end = funcs.find(",");
            if(pos_end == string::npos)
                pos_end = funcs.find("}");
            while (pos_end != string::npos)
            {
                func_param = funcs.substr(0, pos_end);
                if(func_param == "" || func_param.find("&") == string::npos)
                  break;
                string func = func_param.substr(1, func_param.find("&")-1);
                int param = stoi(func_param.substr(func_param.find("&") + 1), nullptr, 0);
                ConfigInfos.insert(pair<string, int>(func, param));
                funcs = funcs.substr(pos_end + 1);
                pos_end = funcs.find(",");
                if(pos_end == string::npos)
                pos_end = funcs.find("}");
            }
        }
    }
}
vector<Expr *> UnInitVarChecker::selectExpr(vector<CallExpr *> membercall_exprs)
{
    vector<Expr *> selected_exprs;
    if (membercall_exprs.empty() || ConfigInfos.empty())
        return selected_exprs;
    for (CallExpr *membercall_expr : membercall_exprs)
    {   
        // membercall_expr->dump();
        if (membercall_expr->getCallee()->getStmtClass() == clang::Stmt::MemberExprClass)
            {
                auto it = ConfigInfos.find(membercall_expr->getDirectCallee()->getNameAsString());
                MemberExpr *member_expr = llvm::dyn_cast<MemberExpr>(membercall_expr->getCallee());
                if ((it != ConfigInfos.end()) && (*it).second <= (membercall_expr->getNumArgs() - 1) &&
                    (membercall_expr->getArg((*it).second))){
                    if (extract_simple_value_expr(membercall_expr->getArg((*it).second))->getStmtClass() == clang::Stmt::DeclRefExprClass)
                    {
                        selected_exprs.push_back(const_cast<Expr *>(extract_simple_value_expr(membercall_expr->getArg((*it).second))));
                        break;
                    }
                    else if (membercall_expr->getArg((*it).second)->getStmtClass() == clang::Stmt::ImplicitCastExprClass){
                        ImplicitCastExpr *tmp_expr = llvm::dyn_cast<ImplicitCastExpr>(membercall_expr->getArg((*it).second));
                        selected_exprs.push_back(tmp_expr->getSubExpr());
                        break;
                    }
                }
            }
    }
    return selected_exprs;
}

MemberExpr *UnInitVarChecker::getMemberExprByFieldDecl(FieldDecl *field_decl, string filename, FunctionDecl *func_decl) //换个遍历类TODO 首先遍历所在函数
{
    FindMemberExpr *findmember = new FindMemberExpr(field_decl);
    findmember->TraverseStmt(func_decl->getBody());
    if (findmember->getMemberExpr() != nullptr)
        return findmember->getMemberExpr();
    ASTFunction *ast_func = call_graph->getFunction(func_decl);
    for (ASTFunction *AF : call_graph->getParents(ast_func))
    {
        FunctionDecl *FD = manager->getFunctionDecl(AF);
        findmember->TraverseStmt(FD->getBody());
        if (findmember->getMemberExpr() != nullptr)
            return findmember->getMemberExpr();
    }
    return nullptr;
}

bool FindStructUse::VisitCXXMemberCallExpr(CXXMemberCallExpr *membercall_expr) //如果参数里面有struct类型则返回，对符合条件的memberexprs再一次进行精确筛选
{
    if (membercall_expr)
    {
        CallExpr *call_expr = llvm::dyn_cast<CallExpr>(membercall_expr);
        for (unsigned i = 0; i < call_expr->getNumArgs(); i++)
        {
            if (call_expr->getArg(i)->getType().getAsString().find("struct") != string::npos)
                container_struct.push_back(call_expr);
            return true;
        }
    }
    return true;
}

vector<MemberExpr *> get_bop_member(MemberExpr *member_expr)
{
    vector<MemberExpr *> member_expr_vector;
    member_expr_vector.push_back(member_expr);
    if (remove_cast(member_expr->getBase())->getStmtClass() == clang::Stmt::MemberExprClass)
    {
        MemberExpr *member_expr_base = llvm::dyn_cast<MemberExpr>(remove_cast(member_expr->getBase()));
        member_expr_vector = get_bop_member(member_expr_base);
        member_expr_vector.push_back(member_expr);
    }
    else if (remove_cast(member_expr->getBase())->getStmtClass() == clang::Stmt::ArraySubscriptExprClass)
    {
        clang::ArraySubscriptExpr *array_tmp = llvm::dyn_cast<clang::ArraySubscriptExpr>(remove_cast(member_expr->getBase()));
        Expr *array_base = remove_cast(array_tmp->getBase());
        if(array_base->getStmtClass() == clang::Stmt::DeclRefExprClass)
          return member_expr_vector;
        else if(array_base->getStmtClass() == clang::Stmt::MemberExprClass)
        {
            MemberExpr *member_tmp = llvm::dyn_cast<clang::MemberExpr>(array_base);
            member_expr_vector = get_bop_member(member_tmp);
            member_expr_vector.push_back(member_expr);
        }
          
    }
    else
    {
        return member_expr_vector;
    }
    return member_expr_vector;
}

bool FindStructUseCut::VisitBinaryOperator(BinaryOperator *bOP)
{

    if (bOP)
    {
        if (bOP->getOpcode() == clang::BinaryOperatorKind::BO_Assign)
        {
            // bOP->getLHS()->dump();
            // bOP = extract_simple_value_expr(bOP->getLHS())
            Expr *simple_expr = const_cast<Expr *>(extract_simple_value_expr(bOP->getLHS()));

            if (simple_expr && simple_expr->getStmtClass() == clang::Stmt::MemberExprClass)
            {
                MemberExpr *expr = llvm::dyn_cast<MemberExpr>(simple_expr); //剔除在bop左值的memberexprq
                if (expr->getType().getAsString().find("anonymous") == string::npos)
                {
                    vector<MemberExpr *> bop_members = get_bop_member(expr);
                    for (auto it : bop_members)
                        cutExprs.push_back(it);
                }
            }
            else if (bOP->getLHS()->getStmtClass() == clang::Stmt::ArraySubscriptExprClass)
            {
                ArraySubscriptExpr *tmp = llvm::dyn_cast<ArraySubscriptExpr>(bOP->getLHS());
                clang::Expr *lhs = tmp->getLHS();
                if (MemberExpr *mem_tmp = llvm::dyn_cast<MemberExpr>(remove_cast(lhs)))
                {
                    vector<MemberExpr *> bop_members = get_bop_member(mem_tmp);
                    for (auto it : bop_members)
                        cutExprs.push_back(it);
                }
            }
        }
    }
    return true;
}
/*bool FindStructUseCut::VisitUnaryOperator(UnaryOperator *UOP)
{
    if (remove_cast(UOP->getSubExpr())->getStmtClass() == clang::Stmt::MemberExprClass) //剔除引用的func(&a->b)
    {
        if (MemberExpr *mem_tmp = llvm::dyn_cast<MemberExpr>(remove_cast(UOP->getSubExpr())))
        {
            vector<MemberExpr *> bop_members = get_bop_member(mem_tmp);
            for (auto it : bop_members)
                cutExprs.push_back(it);
        }
    }
    return true;
}*/

bool FindStructUseCut::VisitCallExpr(CallExpr *call_expr)
{
    if (remove_cast(call_expr->getCallee())->getStmtClass() == clang::Stmt::MemberExprClass)
    {
        if (MemberExpr *mem_tmp = llvm::dyn_cast<MemberExpr>(remove_cast(call_expr->getCallee())))
            cutExprs.push_back(mem_tmp);
    }
    else if (getStringOfExpr(call_expr).find("memcpy") != string::npos || 
             getStringOfExpr(call_expr).find("memset") != string::npos)
    {
        for (int i = 0; i < call_expr->getNumArgs(); i++)
        {
            if (MemberExpr *mem_tmp = llvm::dyn_cast<MemberExpr>(remove_cast(call_expr->getArg(i))))
            {
              vector<MemberExpr *> bop_members = get_bop_member(mem_tmp);
              for (auto it : bop_members)
                cutExprs.push_back(it);
            }
        }
    }
    return true;
}
